import hashlib
import requests
import json
import os
import time
import sys
import datetime
from urllib.parse import quote

# 获取脚本所在目录
def get_script_dir():
    if getattr(sys, 'frozen', False):  # 如果是打包后的可执行文件
        return os.path.dirname(sys.executable)
    else:  # 如果是脚本文件
        return os.path.dirname(os.path.abspath(__file__))

# 创建基于日期的目录结构
current_date = datetime.datetime.now().strftime("%Y%m%d")               # 获取当前日期字符串（年月日）
script_dir = get_script_dir()
download_dir = os.path.join(script_dir, f"kugou_music_{current_date}")  # 音乐下载目录
json_file_path = os.path.join(download_dir, f"kugou_songs_{current_date}.json")  # JSON文件路径

# 从数据目录下的JSON文件中读取歌曲信息
def load_songs_from_json():
    try:
        with open(json_file_path, 'r', encoding='utf-8') as f:
            return json.load(f)
    except FileNotFoundError:
        print(f"文件 {json_file_path} 不存在，请先运行爬虫脚本获取歌曲列表")
        return []

# 保存歌曲信息到JSON文件（带日期）
def save_songs_to_json(songs):
    with open(json_file_path, 'w', encoding='utf-8') as f:
        json.dump(songs, f, ensure_ascii=False, indent=2)
    print(f"歌曲列表已保存到: {json_file_path}")

# 生成签名
def generate_signature(encode_album_audio_id):
    # 加密参数列表
    s = [
        "NVPh5oo715z5DIWAeQlhMDsWXXQV4hwt",
        "appid=1014",
        f"clienttime={int(time.time() * 1000)}",  # 使用当前时间戳
        "clientver=20000",
        "dfid=1Jc0ko2qzOGQ3y25Xr3mWS4B",
        f"encode_album_audio_id={encode_album_audio_id}",
        "mid=afefb038576a22ec88d7172ef34c6bda",
        "platid=4",
        "srcappid=2919",
        "token=17c84ddd240dea40b05b90ea377f04471380a7406a7b9a241aa81ca179c10831",
        "userid=35163334",
        "uuid=afefb038576a22ec88d7172ef34c6bda",
        "NVPh5oo715z5DIWAeQlhMDsWXXQV4hwt"
    ]
    
    # 把列表合并成字符串
    string = ''.join(s)
    
    # MD5加密处理
    signature = hashlib.md5(string.encode('utf-8')).hexdigest()
    return signature

# 获取歌曲信息
def get_song_info(encode_album_audio_id):
    headers = {
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36',
        'Referer': 'https://www.kugou.com/',
    }
    
    # 生成签名
    signature = generate_signature(encode_album_audio_id)
    
    params = {
        'srcappid': '2919',
        'clientver': '20000',
        'clienttime': str(int(time.time() * 1000)),
        'mid': 'afefb038576a22ec88d7172ef34c6bda',          # cookie的mid值
        'uuid': 'afefb038576a22ec88d7172ef34c6bda',         # cookie的mid值
        'dfid': '1Jc0ko2qzOGQ3y25Xr3mWS4B',                 # cookie的dfid值
        'appid': '1014',                                    # cookie的appid值
        'platid': '4',
        'encode_album_audio_id': encode_album_audio_id,     # 歌曲ID
        'token': '3932b535940e4958d7a12cb41c495363ba596a93912009a0ab82cb54d326d2eb',    # cookie的token值
        'userid': '35163334',                               # cookie的userid值
        'signature': signature,
    }
    
    try:
        response = requests.get('https://wwwapi.kugou.com/play/songinfo', params=params, headers=headers, timeout=10)
        response.raise_for_status()
        return response.json()
    except Exception as e:
        print(f"获取歌曲信息失败: {str(e)}")
        return None

# 下载歌曲
def download_song(url, filename):
    try:
        headers = {
            'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36',
        }
        
        response = requests.get(url, headers=headers, stream=True, timeout=30)
        response.raise_for_status()
        
        # 清理文件名中的非法字符
        invalid_chars = '<>:"/\\|?*'
        for char in invalid_chars:
            filename = filename.replace(char, '_')
        
        # 限制文件名长度
        if len(filename) > 100:
            filename = filename[:100]
        
        filepath = os.path.join(download_dir, f"{filename}.mp3")
        
        # 如果文件已存在，添加数字后缀
        counter = 1
        original_filepath = filepath
        while os.path.exists(filepath):
            name, ext = os.path.splitext(original_filepath)
            filepath = f"{name}_{counter}{ext}"
            counter += 1
        
        with open(filepath, 'wb') as f:
            for chunk in response.iter_content(chunk_size=8192):
                if chunk:
                    f.write(chunk)
        
        print(f"已下载: {os.path.basename(filepath)}")
        return True
    except Exception as e:
        print(f"下载失败: {filename}, 错误: {str(e)}")
        return False

# 爬取热门榜单
def crawl_hot_list():
    # 要下载的歌曲榜单
    hot_list = [
        # 酷狗飙升榜
        "https://www.kugou.com/yy/rank/home/1-6666.html?from=rank",
        # 酷狗TOP500
        "https://www.kugou.com/yy/rank/home/1-8888.html?from=rank",
        # 蜂鸟流行音乐榜
        "https://www.kugou.com/yy/rank/home/1-59703.html?from=rank",
        # 抖音热歌榜
        "https://www.kugou.com/yy/rank/home/1-52144.html?from=rank",
        # 快手热歌榜
        "https://www.kugou.com/yy/rank/home/1-52767.html?from=rank",
        # DJ热歌榜
        "https://www.kugou.com/yy/rank/home/1-24971.html?from=rank",
        # 内地榜
        "https://www.kugou.com/yy/rank/home/1-31308.html?from=rank",
        # 香港地区榜
        "https://www.kugou.com/yy/rank/home/1-31313.html?from=rank"
    ]
    
    headers = {
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36',
    }
    
    results = []
    seen_eids = set()
    
    for index, url in enumerate(hot_list):
        print(f"正在爬取第 {index+1}/{len(hot_list)} 个榜单: {url}")
        
        try:
            response = requests.get(url, headers=headers, timeout=10)
            response.encoding = 'utf-8'
            
            # 使用lxml解析HTML
            from lxml import html
            tree = html.fromstring(response.text)
            
            # 使用XPath选择所有符合要求的li元素
            li_list = tree.xpath('//div[@id="rankWrap"]/div[2]/ul/li')
            
            # 提取每个li元素的title和data-eid属性
            for li in li_list:
                title = li.get('title')
                data_eid = li.get('data-eid')
                
                # 检查是否成功获取到数据且data_eid不重复
                if title and data_eid and data_eid not in seen_eids:
                    seen_eids.add(data_eid)
                    results.append({
                        'title': title,
                        'data-eid': data_eid,
                        'source': url.split('/')[-1].split('.')[0]  # 记录来源榜单
                    })
            
            print(f"  从该榜单获取到 {len([li for li in li_list if li.get('title') and li.get('data-eid')])} 首歌曲")
            
            # 添加延迟，避免请求过于频繁
            time.sleep(1)
            
        except Exception as e:
            print(f"爬取 {url} 时出错: {str(e)}")
            continue
    
    return results

# 主函数
def main():
    # 确保目录存在
    if not os.path.exists(download_dir):
        os.makedirs(download_dir)
    
    print(f"当前日期: {current_date}")
    print(f"下载目录: {download_dir}")
    
    # 检查是否已有歌曲列表
    songs = load_songs_from_json()
    
    if not songs:
        print("没有找到歌曲列表，开始爬取热门榜单...")
        songs = crawl_hot_list()
        
        if songs:
            save_songs_to_json(songs)
        else:
            print("爬取榜单失败，无法继续下载")
            return
    
    print(f"找到 {len(songs)} 首歌曲，开始下载...")
    print(f"下载目录: {download_dir}")
    
    success_count = 0
    fail_count = 0
    
    for i, song in enumerate(songs):
        print(f"正在处理第 {i+1}/{len(songs)} 首歌曲: {song['title']}")
        
        # 获取歌曲信息
        song_info = get_song_info(song['data-eid'])
        
        if not song_info or song_info.get('err_code') != 0:
            print(f"获取歌曲信息失败: {song['title']}")
            fail_count += 1
            continue
        
        # 提取播放URL
        play_url = song_info.get('data', {}).get('play_url')
        if not play_url:
            print(f"未找到歌曲播放URL: {song['title']}")
            fail_count += 1
            continue
        
        # 下载歌曲
        filename = f"{song['title']}"
        if download_song(play_url, filename):
            success_count += 1
        else:
            fail_count += 1
        
        # 添加延迟，避免请求过于频繁
        time.sleep(1)
    
    print(f"\n下载完成! 成功: {success_count}, 失败: {fail_count}")
    print(f"歌曲已保存到: {download_dir}")
    print(f"歌曲列表已保存到: {json_file_path}")

if __name__ == "__main__":
    main()